// JORGE FERNANDO GOMEZ
// 1259371
// 189631
// LEC A1
// MARTIN MÜLLER
// D02
// YANG LIU


#define _POSIX_C_SOURCE 200112L

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <sys/time.h>

#include <math.h>
#include <curses.h>

#include "gamestate.h"

#include "hash.h"
#include "util.h"
#include "keyboardagent.h"


/* Mask to use with sigprocmask(). */
static sigset_t landerBlockMask;

/* Holds the latest pressed key by the user. */
static int pressedKey = '\0';

/* Velocity limit for the lander to land successfully or crash. */
static double threshold_velocity = 20.00;

/* Values susceptible to command line arguments. */

    /* Holds the points of the landscape figure. */
    static struct worldLandscape * landscape = NULL;

    /* Gravity for the gameworld. */
    static double worldGravity = 0.0;

    /* Value for the lander thrusters for the gameworld. */
    static double landerThrusts = -0.0;

    /* Booleans for using bounce or wrap in the game. */
    static bool bounce = false;
    static bool wrap = false;


/* Amount of landers created so far. */
static int LanderCount = 0;

/* Amount of wrap landers created so far. */
static int wrap_counter = 0;

/* Hash table pointer that will hold lander objects. */
static hash * Landers = NULL;


/*  0    => Not landed yet.
 *  1    => Landed safely.
 * -1    => Crashed. */
static int landingStatus = 0;


#if GAMESTATE_DEBUG
    /* Stream for debugging. */
    FILE * logFile = NULL;
#endif


/* FUNCTIONS */

/* setLanderMask() just initializes landerBlockMask and
 * adds SIGALRM to the set. */
void setLanderMask ( void ) {
    sigemptyset(&landerBlockMask);
    sigaddset(&landerBlockMask, SIGALRM);
}

/* getLanderMask() returns the sigset_t landerBlockMask. */
void* getLanderMask ( void ) {
    return &landerBlockMask;
}

/* Creates the gamestate.c hash table Landers. */
void initLandersHash ( void ) {
    Landers = newHashTable(LAND_HASHT_SIZE);
}

/* newLander() creates a new lander object figure.
 * It initializes the landerBlockMask set, the Landers hash table
 * if LanderCount is 0. Assigns all the given arguments to their
 * respective struct members, and also computes the terminal velocity
 * for the lander at the given beginning height.
 * newLander() also initializes the lander figure on the Sketchpad
 * screen (draws it). */
LanderFigure* newLander ( char * name, double side_length,
                          double x_offset, double y_offset ) {

    if ( LanderCount == 0 ) {
        setLanderMask();
        initLandersHash();

#if GAMESTATE_DEBUG
        logFile = getLogStream();
#endif

    }

    LanderFigure * lander = calloc(1, sizeof(LanderFigure));

    if ( lander == NULL )
        killim(__LINE__);


    /* Set lander's attributes. */
    strcpy(lander->name, name);

    lander->side_length = side_length;

    lander->x_offset = x_offset;
    lander->y_offset = y_offset;
    lander->x_offset_old = x_offset;
    lander->y_offset_old = y_offset;

    lander->velocity_x = 0.0;
    lander->velocity_y = 0.0;

    lander->thrust_x = 0.0;
    lander->thrust_y = 0.0;
    lander->thrust_on = false;

    lander->terminal_velocity = sqrt( 2.0 * worldGravity * (WORLD_HEIGHT - y_offset) );

    lander->orientation = 90.00;

    memset(lander->landerSegments, 0, sizeof(LanderLocationCoords) * MAX_SHAPE_SEGMENTS);

    LanderCount++;

    /* Draw lander to the screen for the first time. */
    initLanderFigure(lander);

    /* Add lander to lander's hash. */
    addLander(name, lander);

    if ( wrap == true && wrap_counter == 0 ) {
        wrap_counter++;
        newLander("wrapLanderRight", side_length, (-side_length/2.0), (-side_length));
        newLander("wrapLanderLeft",  side_length, WORLD_WIDTH, (-side_length));
    }

    return lander;
}

/* initLanderFigure() draws a lander object figure for the first time on
 * the Sketchpad screen. */
void initLanderFigure ( LanderFigure * lander ) {

    FILE ** outputStream = getOutputStream();

    /* Calculate each corner point with triangles. */

    /* Create all of the lander's corner points around the origin. */
    double side_length = lander->side_length;
    double height = side_length;

    double x_offset = lander->x_offset;
    double y_offset = lander->y_offset;

    /* Build abc triangle, a = b = side_length/2. */
    double topLeftCorner_x = NEGATIVE side_length/4;
    double topLeftCorner_y = NEGATIVE height/2;

    double topRightCorner_x = side_length/4;
    double topRightCorner_y = NEGATIVE height/2;

    double bottomLeftCorner_x = NEGATIVE side_length/2;
    double bottomLeftCorner_y = height/2;

    double bottomRightCorner_x = side_length/2;
    double bottomRightCorner_y = height/2;

    lander->segmentCount = 4;

        lander->landerSegments[0].x = topLeftCorner_x;
        lander->landerSegments[0].y = topLeftCorner_y;
        lander->landerSegments[0].dx = topRightCorner_x;
        lander->landerSegments[0].dy = topRightCorner_y;

        lander->landerSegments[1].x = topRightCorner_x;
        lander->landerSegments[1].y = topRightCorner_y;
        lander->landerSegments[1].dx = bottomRightCorner_x;
        lander->landerSegments[1].dy = bottomRightCorner_y;

        lander->landerSegments[2].x = bottomRightCorner_x;
        lander->landerSegments[2].y = bottomRightCorner_y;
        lander->landerSegments[2].dx = bottomLeftCorner_x;
        lander->landerSegments[2].dy = bottomLeftCorner_y;

        lander->landerSegments[3].x = bottomLeftCorner_x;
        lander->landerSegments[3].y = bottomLeftCorner_y;
        lander->landerSegments[3].dx = topLeftCorner_x;
        lander->landerSegments[3].dy = topLeftCorner_y;


    /* Draw lander on sketchpad with shifted coordinates (offsets). */
    fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( topLeftCorner_x     + x_offset ),
            lround( topLeftCorner_y     + y_offset ),
            lround( topRightCorner_x    + x_offset ),
            lround( topRightCorner_y    + y_offset ));
    fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( topRightCorner_x    + x_offset ),
            lround( topRightCorner_y    + y_offset ),
            lround( bottomRightCorner_x + x_offset ),
            lround( bottomRightCorner_y + y_offset ));
    fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( bottomRightCorner_x + x_offset ),
            lround( bottomRightCorner_y + y_offset ),
            lround( bottomLeftCorner_x  + x_offset ),
            lround( bottomLeftCorner_y  + y_offset ));
    fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( bottomLeftCorner_x  + x_offset ),
            lround( bottomLeftCorner_y  + y_offset ),
            lround( topLeftCorner_x     + x_offset ),
            lround( topLeftCorner_y     + y_offset ));

    fflush(*outputStream);
}

/* addLander() defines the key name in the Landers hash table.
 * The actual object lander is stored as this key's value in the hash. */
void addLander ( char * name, LanderFigure * lander ) {
    hashInsert(Landers, name, lander);
}

/* updateLander() takes a name argument (to retrieve the appropiate lander,
 * and a slope (x,y shifts).  It also takes a rotate direction LEFT or RIGHT).
 * The x and y shifts represent differences in the new to be updated lander
 * position, and the old already stored lander position.  updateLander() adds
 * this shift values to the stored values and redraws the lander.
 * updateLander() also calls rotateLander() on every call using the
 * given rotate_direction as argument for rotateLander as well. */
void updateLander ( char * name, double x_shift, double y_shift,
                    int rotate_direction ) {

    LanderFigure * lander = hashGetValue(Landers, name);
    eraseLander(lander);

    lander->x_offset += x_shift;
    lander->y_offset += y_shift;

    LanderFigure * baseLander = hashGetValue(Landers, "baseLander");
    rotateLander(baseLander, lander, rotate_direction);

    drawLander(lander);
}

/* drawLander() retrieves the pairs of points stored at each index of
 * lander->landerSegments array (which represent the lander figure) and
 * sends drawSegment commands with each of these pairs plus the stored
 * x and y offsets.  It uses lround() to convert the double values to
 * longs for Sketchpad to recognize. */
void drawLander ( LanderFigure * lander ) {
    /* */

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t\tIn drawLander()...\n");
#endif

    FILE ** outputStream = getOutputStream();

    double x_offset = lander->x_offset;
    double y_offset = lander->y_offset;

    int i;
    for ( i = 0; i < lander->segmentCount; i++ ) {
        fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( lander->landerSegments[i].x     + x_offset ),
            lround( lander->landerSegments[i].y     + y_offset ),
            lround( lander->landerSegments[i].dx    + x_offset ),
            lround( lander->landerSegments[i].dy    + y_offset ));

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tdrawing (%ld, %ld) to (%ld, %ld)\n",
            lround( lander->landerSegments[i].x     + x_offset ),
            lround( lander->landerSegments[i].y     + y_offset ),
            lround( lander->landerSegments[i].dx    + x_offset ),
            lround( lander->landerSegments[i].dy    + y_offset ));
        fflush(logFile);
#endif

    }

    fflush(*outputStream);
}

/* eraseLander() retrieves the pairs of points stored at each index of
 * lander->landerSegments array (which represent the lander figure) and
 * sends eraseSegment commands with each of these pairs plus the stored
 * x and y offsets.  It uses lround() to convert the double values to
 * longs for Sketchpad to recognize. */
void eraseLander ( LanderFigure * lander ) {

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t\tIn eraseLander()...\n");
#endif

    FILE ** outputStream = getOutputStream();

    double x_offset = lander->x_offset;
    double y_offset = lander->y_offset;

    int i;
    for ( i = 0; i < lander->segmentCount; i++ ) {
        fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( lander->landerSegments[i].x     + x_offset ),
            lround( lander->landerSegments[i].y     + y_offset ),
            lround( lander->landerSegments[i].dx    + x_offset ),
            lround( lander->landerSegments[i].dy    + y_offset ));

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\terasing (%ld, %ld) to (%ld, %ld)\n",
            lround( lander->landerSegments[i].x     + x_offset ),
            lround( lander->landerSegments[i].y     + y_offset ),
            lround( lander->landerSegments[i].dx    + x_offset ),
            lround( lander->landerSegments[i].dy    + y_offset ));
        fflush(logFile);
#endif

    }
}

/* rotateLander() rotates a baseLander object figure in rotate_direction
 * (LEFT or RIGHT).  It uses DEFAULT_ANGLE as the rotating factor.
 * baseLander never actually changes orientation (it is always upright), and
 * thus the rotation is always exactly from 90 to whatever orientation our
 * lander needs to be in at this specific timestep (this is to avoid
 * floating-point errors).  It stores the end result of rotating the
 * baseLander in the actual lander's lander->landerSegments. */
void rotateLander ( LanderFigure * baseLander, LanderFigure * lander,
                    int rotate_direction ) {

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t\tIn rotateLander()...\n");
        fflush(logFile);
#endif

    double angle = DEFAULT_ANGLE;
    baseLander->orientation = ( (long) ( baseLander->orientation +
                                 rotate_direction*angle )) % 360;
    double angleRadians = ((baseLander->orientation - 90.0) * (PI)) / 180.0;

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tbaseLander->orientation('%lf')\n",
                                baseLander->orientation);
        fprintf(logFile, "\t\t\torientation |--> radians('%lf')\n",
                                angleRadians);
        fflush(logFile);
#endif

    int segmentCount = lander->segmentCount;

    double x, y;
    double rx, ry;


#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tbeginning rotation...\n");
        fflush(logFile);
#endif

    int i;
    for ( i = 0; i < segmentCount; i++ ) {
        x = baseLander->landerSegments[i].x;
        y = baseLander->landerSegments[i].y;

        rx = (x * cos(angleRadians)) - (sin(angleRadians) * y);
        ry = (x * sin(angleRadians)) + (cos(angleRadians) * y);

#if GAMESTATE_DEBUG
            fprintf(logFile, "\t\t\t\tStoring (%lf, %lf) to",
                                rx, ry);
            fflush(logFile);
#endif

        lander->landerSegments[i].x = rx;
        lander->landerSegments[i].y = ry;


        x = baseLander->landerSegments[i].dx;
        y = baseLander->landerSegments[i].dy;

        rx = (x * cos(angleRadians)) - (sin(angleRadians) * y);
        ry = (x * sin(angleRadians)) + (cos(angleRadians) * y);

#if GAMESTATE_DEBUG
            fprintf(logFile, " (%lf, %lf)\n",
                                rx, ry);
            fprintf(logFile, "\n");
            fflush(logFile);
#endif

        lander->landerSegments[i].dx = rx;
        lander->landerSegments[i].dy = ry;

    }
}

/* landerComputeVerticalVelocity() computes a new y-velocity for the
 * LanderFigure pointed to by lander.  It returns this computed value. */
double landerComputeVerticalVelocity ( LanderFigure * lander ) {

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t\tIn landerComputeVerticalVelocity()\n");
        fprintf(logFile, "\n");
        fflush(logFile);

        fprintf(logFile, "\t\t\told velocity_y('%lf')\n", lander->velocity_y);
#endif

    double velocity_y = lander->velocity_y + (
            (lander->thrust_y + worldGravity) * TIMESTEP_SEC);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tnew velocity_y('%lf')\n", velocity_y);
        fflush(logFile);

        fprintf(logFile, "\n\t\t\treturning velocity_y\n");
#endif

    return velocity_y;
}

/* landerComputeHorizontalVelocity() computes a new x-velocity for the
 * LanderFigure pointed to by lander.  It returns this computed value. */
double landerComputeHorizontalVelocity ( LanderFigure * lander ) {

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t\tIn landerComputeHorizontalVelocity()\n");
        fprintf(logFile, "\n");
        fflush(logFile);

        fprintf(logFile, "\t\t\told velocity_x('%lf')\n", lander->velocity_x);
#endif

    double velocity_x = lander->velocity_x;
    double acceleration_x = lander->thrust_x;

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tnew velocity_x('%lf')\n", velocity_x);
        fflush(logFile);

        fprintf(logFile, "\n\t\t\treturning velocity_x\n");
#endif

    return (velocity_x + (acceleration_x * TIMESTEP_SEC));
}

/* landerComputeThrust() computes new x,y-velocities for the lander
 * with name name depending on thrust value and the lander orientation.
 * It updates these computed values for that lander in the end. */
void landerComputeThrust ( char * name ) {

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t\tIn landerComputeThrust()\n");
        fprintf(logFile, "\n");
#endif

    LanderFigure * lander = hashGetValue(Landers, name);
    LanderFigure * baseLander = hashGetValue(Landers, "baseLander");

    double orientation = baseLander->orientation;
    double rad_orientation = (PI * orientation)/180;

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\torientation('%lf'), rad_orientation('%lf')\n",
                              orientation, rad_orientation );
#endif

    /*+ Compute the y,x-accelerations generated from the thruster. +*/

    /* Y-THRUST. */
    double thrust_y = worldGravity + (landerThrusts * sin(rad_orientation));

    double velocity_y = lander->velocity_y + (thrust_y * TIMESTEP_SEC);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tthrust_y('%lf'), velocity_y('%lf')\n",
                             thrust_y, velocity_y );
#endif

    lander->velocity_y = velocity_y;
    lander->thrust_y = thrust_y;

    /* X-THRUST. */
    double thrust_x = landerThrusts * cos(rad_orientation);
    double velocity_x = lander->velocity_x + (thrust_x * TIMESTEP_SEC);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\t\t\tthrust_x('%lf'), velocity_x('%lf')\n",
                             thrust_x, velocity_x );
#endif

    lander->velocity_x = velocity_x;
    lander->thrust_x = thrust_x;
}

/* landerTimeStep() is the handling function for each SIGALRM received
 * by this program.  What landerTimeStep represents is the passing of
 * time for our lander program.  Each timestep is 50.000 microseconds
 * long.  At each time step, a new velocity and position for the
 * control lander are computed.  These values are computed using the
 * basic Newtonian physics formulas.  landerTimeStep() actually
 * updates the drawing of the lander using the updateLander() function.
 * updateLander() takes a name, x,y-axis shifts, and a rotate direction.
 * landerTimeStep() calls this updateLander() function with  no rotate
 * direction, and the x,y-axis shifts are the following.  A new x-position
 * is computed, then the x-shift is the difference between the old
 * x-position and the new x-position.  Same for the y component.
 * After these calculations are done, landerTimeStep() uses the function
 * lineSegmentIntersection() to evaluate if there are segment colissions
 * between the lander figure and the landscape figure.
 * All of the code happening in this function is protected by blocking
 * any generated SIGALRM signals (unblocked in the end). */
void landerTimeStep ( int sig ) {

    if ( sig != SIGALRM )
        return;

    if (LanderCount == 0)
        return;

    /* Set up a mask to protect code. */
    sigset_t oldMask;
    sigprocmask(SIG_BLOCK, &landerBlockMask, &oldMask);


#if GAMESTATE_DEBUG
        /* Debugging stuff. */
        static int iteration = 0;

        fprintf(logFile, "\n");
        fprintf(logFile, "/*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*\n");
        fprintf(logFile, " *   In landerTimeStep('%5d')...   *\n", iteration);
        fprintf(logFile, " *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/\n");
        fprintf(logFile, "\n");
        fflush(logFile);
        iteration++;
#endif

    char * name = getLanderControlName();
    LanderFigure * lander = hashGetValue(Landers, name);
    lander->thrust_on = false;
    landerEraseThrust(name);


    /* Input. */
    lander->thrust_x = 0.0;
    lander->thrust_y = 0.0;

    char key_string[6];
    int inputChar = getInput();
    pressedKey = inputChar;

    snprintf(key_string, 6, "0x%x", inputChar);

    hash * dispatchTable = getDispatchTable();
    if ( hashExists(dispatchTable, key_string) == true ) {

#if GAMESTATE_DEBUG
        fprintf(logFile, "\tcalling dispatchTable{'%s'}->doAction()\n", key_string);
#endif

        ((LAction) hashGetValue(dispatchTable, key_string))->doAction();

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n");
        fflush(logFile);
#endif

    }

    if ( bounce == true )
        landerComputeBounce(lander);
    else if ( wrap == true )
        landerComputeWrap(name);

    /* Calculate new positions and velocities for x,y-components. */
    double velocity_x = 0;
    double velocity_y = 0;


#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t/*** Horizontal Component ***/\n");
#endif

    /* Horizontal component. */
    double x_offset_old = lander->x_offset;
    lander->x_offset_old = x_offset_old;
    double x_offset_new = 0.0;

    x_offset_new = x_offset_old +
                          lander->velocity_x * TIMESTEP_SEC +
                          0.5 * lander->thrust_x * pow(TIMESTEP_SEC, 2);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\tcalling landerComputeHorizontalVelocity(lander)\n");
#endif

    velocity_x = landerComputeHorizontalVelocity(lander);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n");
        fflush(logFile);
#endif


    double x_offset_diff = x_offset_new - x_offset_old;

#if GAMESTATE_DEBUG
        fprintf(logFile, "\tx_offset_diff('%lf') = x_offset_new('%lf') - x_offset_old('%lf')\n",
                            x_offset_diff, x_offset_new, x_offset_old );
        fprintf(logFile, "\n");
        fflush(logFile);
#endif


    lander->velocity_x = velocity_x;


#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\t/*** Vertical Component ***/\n");
#endif

    /* Vertical component. */
    double y_offset_old = lander->y_offset;
    lander->y_offset_old = y_offset_old;
    double y_offset_new = 0.0;
    if ( lander->velocity_y < lander->terminal_velocity ) {
        y_offset_new = y_offset_old +
                              lander->velocity_y * TIMESTEP_SEC +
                              0.5 * worldGravity * pow(TIMESTEP_SEC, 2);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\tcalling landerComputeVerticalVelocity(lander)\n");
#endif

        velocity_y = landerComputeVerticalVelocity(lander);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n");
        fflush(logFile);
#endif

        lander->velocity_y = velocity_y;
    }
    else {
        y_offset_new = y_offset_old +
                              lander->velocity_y * TIMESTEP_SEC +
                              0.5 * worldGravity * pow(TIMESTEP_SEC, 2);
#if GAMESTATE_DEBUG
        fprintf(logFile, "\tnot-calling lCVV(), (terminal-velocity reached; nothing to do\n");
        fprintf(logFile, "\n");
        fflush(logFile);
#endif

    }

    double y_offset_diff = y_offset_new - y_offset_old;

#if GAMESTATE_DEBUG
        fprintf(logFile, "\ty_offset_diff('%lf') = y_offset_new('%lf') - y_offset_old('%lf')\n",
                            y_offset_diff, y_offset_new, y_offset_old );
        fprintf(logFile, "\n");
        fflush(logFile);


        fprintf(logFile, "\tcalling updateLander('%s', '%lf', '%lf', 0)\n",
                            name, velocity_x, y_offset_diff);
#endif

    updateLander(name, x_offset_diff, y_offset_diff, 0);
    if ( lander->thrust_on == true )
        landerDrawThrust(name);

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n");
        fflush(logFile);
#endif

    LanderFigure * baseLander = hashGetValue(Landers, "baseLander");
    bool intersected = false;
    double lander_x1, lander_y1;
    double lander_x2, lander_y2;
    double landscape_x1, landscape_y1;
    double landscape_x2, landscape_y2;
    double intersect_x, intersect_y;

    intersect_x = -1.0;
    intersect_y = -1.0;

#if GAMESTATE_DEBUG
        fprintf(logFile, "\n\tChecking for intersections... (for loop)\n");
        fprintf(logFile, "\n");
        fflush(logFile);
#endif

    int i, j;
    for ( i = 0; i < lander->segmentCount; i++ ) {
        for ( j = 0; j < landscape->drawCount-1; j++ ) {
            lander_x1 = lander->landerSegments[i].x     + x_offset_new;
            lander_y1 = lander->landerSegments[i].y     + y_offset_new;
            lander_x2 = lander->landerSegments[i].dx    + x_offset_new;
            lander_y2 = lander->landerSegments[i].dy    + y_offset_new;

            landscape_x1 = landscape->dx[j];
            landscape_y1 = landscape->dy[j];
            landscape_x2 = landscape->dx[j+1];
            landscape_y2 = landscape->dy[j+1];

#if GAMESTATE_DEBUG
                 fprintf(logFile, "\t\tlander(%lf, %lf) to (%lf, %lf)\n",
                         lander_x1, lander_y1, lander_x2, lander_y2);
                 fprintf(logFile, "\t\tlandsc(%lf, %lf) to (%lf, %lf)\n",
                         landscape_x1, landscape_y1, landscape_x2, landscape_y2);
                 fflush(logFile);
#endif

            intersected =
                lineSegmentIntersection( lander_x1,     lander_y1,
                                         lander_x2,     lander_y2,

                                         landscape_x1,  landscape_y1,
                                         landscape_x2,  landscape_y2,

                                         &intersect_x,  &intersect_y );

#if GAMESTATE_DEBUG
                fprintf(logFile, "\n\t\tintersected(%lf, %lf)\n",
                        intersect_x, intersect_y);
                fprintf(logFile, "\t\tintersected('%d')\n", intersected);
                fprintf(logFile, "\n");
                fflush(logFile);
#endif

            if ( intersected == true ) {
                if ( landscape_y1 != landscape_y2 ||
                     velocity_y > threshold_velocity ||
                     velocity_x > threshold_velocity ||
                     baseLander->orientation != 90.00 )
                    landingStatus = -1;
                else
                    landingStatus = 1;
            }

        }

    }
    sigprocmask(SIG_SETMASK, &oldMask, NULL);
}

//  public domain function by Darel Rex Finley, 2006



//  Determines the intersection point of the line segment defined by points A and B
//  with the line segment defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineSegmentIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

    double  distAB, theCos, theSin, newX, ABpos ;

    //  Fail if either line segment is zero-length.
    if ((Ax==Bx && Ay==By) || (Cx==Dx && Cy==Dy)) return false;

    //  Fail if the segments share an end-point.
    if ((Ax==Cx && Ay==Cy) || (Bx==Cx && By==Cy)
    ||  (Ax==Dx && Ay==Dy) || (Bx==Dx && By==Dy)) {
    return false; }

    //  (1) Translate the system so that point A is on the origin.
    Bx-=Ax; By-=Ay;
    Cx-=Ax; Cy-=Ay;
    Dx-=Ax; Dy-=Ay;

    //  Discover the length of segment A-B.
    distAB=sqrt(Bx*Bx+By*By);

    //  (2) Rotate the system so that point B is on the positive X axis.
    theCos=Bx/distAB;
    theSin=By/distAB;
    newX=Cx*theCos+Cy*theSin;
    Cy  =Cy*theCos-Cx*theSin; Cx=newX;
    newX=Dx*theCos+Dy*theSin;
    Dy  =Dy*theCos-Dx*theSin; Dx=newX;

    //  Fail if segment C-D doesn't cross line A-B.
    if ((Cy<0. && Dy<0.) || (Cy>=0. && Dy>=0.)) return false;

    //  (3) Discover the position of the intersection point along line A-B.
    ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

    //  Fail if segment C-D crosses line A-B outside of segment A-B.
    if (ABpos<0. || ABpos>distAB) return false;

    //  (4) Apply the discovered position to line A-B in the original coordinate system.
    *X=Ax+ABpos*theCos;
    *Y=Ay+ABpos*theSin;

    //  Success.
    return true;
}

/* Source: http://alienryderflex.com/intersect/ . */


/* landerDrawThrust() draws the thrust figure for the lander figure
 * with name name.  If wrapping is on, it draws the thrust figure for
 * the wrapping landers as well. */
void landerDrawThrust ( char * name ) {
    /* Thruster is represented as a triangle going outwards from the
     * base of the lander.  The way this is drawn is, draw the triangle
     * in the origin.  Rotate the triangle to correct orientation
     * (using baseLander), and then shift the triangle to the appropiate
     * x,y-offsets. */

    FILE ** outputStream = getOutputStream();

    LanderFigure * lander = hashGetValue(Landers, name);
    LanderFigure * baseLander = hashGetValue(Landers, "baseLander");

    double orientation = baseLander->orientation;
    double angleRadians = ((orientation - 90.0) * (PI)) / 180.0;

    double thrustFigure_x = 0;
    double thrustFigure_y = (3.0/4.0) * (baseLander->side_length);

    double rotatedThrustFigure_x = (thrustFigure_x * cos(angleRadians)) -
                                   (thrustFigure_y * sin(angleRadians));

    double rotatedThrustFigure_y = (thrustFigure_x * sin(angleRadians)) +
                                   (thrustFigure_y * cos(angleRadians));

    fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( lander->landerSegments[2].x  + lander->x_offset ),
            lround( lander->landerSegments[2].y  + lander->y_offset ),
            lround( rotatedThrustFigure_x        + lander->x_offset ),
            lround( rotatedThrustFigure_y        + lander->y_offset ));
    fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( rotatedThrustFigure_x        + lander->x_offset ),
            lround( rotatedThrustFigure_y        + lander->y_offset ),
            lround( lander->landerSegments[2].dx + lander->x_offset ),
            lround( lander->landerSegments[2].dy + lander->y_offset ));

    if ( wrap == true ) {
        LanderFigure * wrapLanderRight = hashGetValue(Landers, "wrapLanderRight");
        LanderFigure * wrapLanderLeft  = hashGetValue(Landers, "wrapLanderLeft");

        fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( wrapLanderRight->landerSegments[2].x  + wrapLanderRight->x_offset ),
            lround( wrapLanderRight->landerSegments[2].y  + wrapLanderRight->y_offset ),
            lround( rotatedThrustFigure_x        + wrapLanderRight->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderRight->y_offset ));
        fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( rotatedThrustFigure_x        + wrapLanderRight->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderRight->y_offset ),
            lround( wrapLanderRight->landerSegments[2].dx + wrapLanderRight->x_offset ),
            lround( wrapLanderRight->landerSegments[2].dy + wrapLanderRight->y_offset ));

        fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( wrapLanderLeft->landerSegments[2].x  + wrapLanderLeft->x_offset ),
            lround( wrapLanderLeft->landerSegments[2].y  + wrapLanderLeft->y_offset ),
            lround( rotatedThrustFigure_x        + wrapLanderLeft->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderLeft->y_offset ));
        fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
            lround( rotatedThrustFigure_x        + wrapLanderLeft->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderLeft->y_offset ),
            lround( wrapLanderLeft->landerSegments[2].dx + wrapLanderLeft->x_offset ),
            lround( wrapLanderLeft->landerSegments[2].dy + wrapLanderLeft->y_offset ));
    }

    fflush(*outputStream);
}

/* landerEraseThrust() erases the thrust figure for the lander figure
 * with name name.  If wrapping is on it erases the thrust figure for
 * the wrapping landers as well. */
void landerEraseThrust ( char * name ) {

    FILE ** outputStream = getOutputStream();

    LanderFigure * lander = hashGetValue(Landers, name);
    LanderFigure * baseLander = hashGetValue(Landers, "baseLander");

    double orientation = baseLander->orientation;
    double angleRadians = ((orientation - 90.0) * (PI)) / 180.0;

    double thrustFigure_x = 0;      // bottomRight_x - delta_x;
    double thrustFigure_y = (3.0/4.0) * (baseLander->side_length);

    double rotatedThrustFigure_x = (thrustFigure_x * cos(angleRadians)) -
                                   (thrustFigure_y * sin(angleRadians));

    double rotatedThrustFigure_y = (thrustFigure_x * sin(angleRadians)) +
                                   (thrustFigure_y * cos(angleRadians));

    fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( lander->landerSegments[2].x  + lander->x_offset ),
            lround( lander->landerSegments[2].y  + lander->y_offset ),
            lround( rotatedThrustFigure_x        + lander->x_offset ),
            lround( rotatedThrustFigure_y        + lander->y_offset ));
    fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( rotatedThrustFigure_x        + lander->x_offset ),
            lround( rotatedThrustFigure_y        + lander->y_offset ),
            lround( lander->landerSegments[2].dx + lander->x_offset ),
            lround( lander->landerSegments[2].dy + lander->y_offset ));

    if ( wrap == true ) {
        LanderFigure * wrapLanderRight = hashGetValue(Landers, "wrapLanderRight");
        LanderFigure * wrapLanderLeft  = hashGetValue(Landers, "wrapLanderLeft");

        fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( wrapLanderRight->landerSegments[2].x  + wrapLanderRight->x_offset ),
            lround( wrapLanderRight->landerSegments[2].y  + wrapLanderRight->y_offset ),
            lround( rotatedThrustFigure_x        + wrapLanderRight->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderRight->y_offset ));
        fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( rotatedThrustFigure_x        + wrapLanderRight->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderRight->y_offset ),
            lround( wrapLanderRight->landerSegments[2].dx + wrapLanderRight->x_offset ),
            lround( wrapLanderRight->landerSegments[2].dy + wrapLanderRight->y_offset ));

        fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( wrapLanderLeft->landerSegments[2].x  + wrapLanderLeft->x_offset ),
            lround( wrapLanderLeft->landerSegments[2].y  + wrapLanderLeft->y_offset ),
            lround( rotatedThrustFigure_x        + wrapLanderLeft->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderLeft->y_offset ));
        fprintf(*outputStream, "eraseSegment %ld %ld %ld %ld\n",
            lround( rotatedThrustFigure_x        + wrapLanderLeft->x_offset ),
            lround( rotatedThrustFigure_y        + wrapLanderLeft->y_offset ),
            lround( wrapLanderLeft->landerSegments[2].dx + wrapLanderLeft->x_offset ),
            lround( wrapLanderLeft->landerSegments[2].dy + wrapLanderLeft->y_offset ));

    }

    fflush(*outputStream);
}

/* landerActivateThrustFlag() sets the thrust_on flag to true
 * for lander object with name name. */
void landerActivateThrustFlag ( char * name ) {
    LanderFigure * lander = hashGetValue(Landers, name);
    lander->thrust_on = true;
}

/* landerComputeBounce() reverses the the velocity_x or velocity_y
 * for lander when it hits an edge of the gameworld (Sketchpad's screen. */
void landerComputeBounce ( LanderFigure * lander ) {
    int i;
    for ( i = 0; i < lander->segmentCount; i++ ) {
        if ( lander->landerSegments[i].x    + lander->x_offset <= 0
            ||
             lander->landerSegments[i].dx   + lander->x_offset <= 0 ) {

            if ( lander->velocity_x < 0 )
                lander->velocity_x = NEGATIVE lander->velocity_x;
            break;
        }
        else
        if ( lander->landerSegments[i].x  + lander->x_offset >= WORLD_WIDTH
            ||
             lander->landerSegments[i].dx + lander->x_offset >= WORLD_WIDTH
             ) {

            if ( lander->velocity_x > 0 )
                lander->velocity_x = NEGATIVE lander->velocity_x;
            break;
        }
        if ( lander->landerSegments[i].y    + lander->y_offset <= 0
            ||
             lander->landerSegments[i].dy   + lander->y_offset <= 0 ) {

            if ( lander->velocity_y < 0 )
                lander->velocity_y = NEGATIVE lander->velocity_y;
            break;
        }
    }
}

/* landerComputeWrap() computes a wrapping lander figure for the lander
 * with name name.  Basically, wrapping is implemented by having two extra
 * landers position at the left/right edge of the Sketchpad screen.  When-
 * ever the control lander is starting to go out of bounds, this extra
 * x-axis offset is added to the corresponding wrapping lander, and is drawed.
 * When the controlling lander has gone out of bounds completely (each of its
 * points is now out of bounds), the controlling lander is assigned the
 * corresponding wrapping lander's x_offset and the wrapping lander is reset
 * to the edge of the screen. */
void landerComputeWrap ( char * name ) {

    /* Retrieve the lander figures needed. */
    LanderFigure * lander     = hashGetValue(Landers, name);
    LanderFigure * baseLander = hashGetValue(Landers, "baseLander");

    LanderFigure * wrapLanderRight = hashGetValue(Landers, "wrapLanderRight");
    LanderFigure * wrapLanderLeft  = hashGetValue(Landers, "wrapLanderLeft");

    eraseLander(wrapLanderRight);
    eraseLander(wrapLanderLeft);

    /* Rotate the wrapping landers to the correct orientation. */
    rotateLander(baseLander, wrapLanderRight, 0);
    rotateLander(baseLander, wrapLanderLeft, 0);

    double extra = 0;
    int counterRight = 0;
    int counterLeft = 0;
    int i;

    /* Compute the amount of points out of bounds for the control lander. */
    for ( i = 0; i < lander->segmentCount; i++ ) {
        if ( lander->landerSegments[i].x  + lander->x_offset >= WORLD_WIDTH
            ||
             lander->landerSegments[i].dx + lander->x_offset >= WORLD_WIDTH
             ) {

            counterRight++;
        }
    }

    if (counterRight == 0) {
        for ( i = 0; i < lander->segmentCount; i++ ) {
            if ( lander->landerSegments[i].x  + lander->x_offset <= 0
                ||
                 lander->landerSegments[i].dx + lander->x_offset <= 0
                 ) {

                counterLeft++;
            }
        }
    }

    /* Compute the amount of x_offset needed for a wrapping lander. */
    extra = lander->x_offset - WORLD_WIDTH + lander->side_length/4 + 1;
    wrapLanderRight->y_offset = lander->y_offset;
    wrapLanderRight->x_offset = extra;
    drawLander(wrapLanderRight);

    extra = lander->x_offset + WORLD_WIDTH - lander->side_length/4 -1;
    wrapLanderLeft->y_offset = lander->y_offset;
    wrapLanderLeft->x_offset = extra;
    drawLander(wrapLanderLeft);

    /* Update the control lander to the appropriate x_offsets,
     * and reset the wrapping landers. */
    if (counterRight >= lander->segmentCount) {
        eraseLander(lander);
        lander->x_offset = wrapLanderRight->x_offset;
        lander->y_offset = wrapLanderRight->y_offset;
        wrapLanderRight->x_offset = (-(wrapLanderRight->side_length)/2.0);
        wrapLanderRight->y_offset = (-(wrapLanderRight->side_length));
    }

    if (counterLeft >= lander->segmentCount) {
        eraseLander(lander);
        lander->x_offset = wrapLanderLeft->x_offset;
        lander->y_offset = wrapLanderLeft->y_offset;
        wrapLanderLeft->x_offset = WORLD_WIDTH;
        wrapLanderLeft->y_offset = (-(wrapLanderLeft->side_length));
    }
}

/* setLandscape() and drawLandscape() store a landscape (set of points)
 * and draw the landscape to the Sketchpad respectively. */

    void setLandscape ( struct worldLandscape * a_landscape ) {
        landscape = a_landscape;
    }

    void drawLandscape ( void ) {
        FILE ** outputStream =  getOutputStream();

        int i;
        for ( i = 0; i < landscape->drawCount-1; i++ )
            fprintf(*outputStream, "drawSegment %ld %ld %ld %ld\n",
                    lround( landscape->dx[i] ),
                    lround( landscape->dy[i] ),
                    lround( landscape->dx[i+1] ),
                    lround( landscape->dy[i+1] ) );
    }

/******************************************************************/


/* setGravity(), setThrust(), setBounce() and setWrap()
 * update the corresponding values for their respective static
 * variables.  Values are read from the command line in lander.c. */

    void setGravity ( double gravity ) {
        worldGravity = gravity;
    }

    void setThrust ( double thrust ) {
        landerThrusts = thrust;
    }

    void setBounce ( bool value ) {
        bounce = value;
        if ( value == true )
            wrap = false;
    }

    void setWrap ( bool value ) {
        wrap = value;
        if ( value == true )
            bounce = false;
    }

/******************************************************************/

/* getPressedKey() returns the int value stored in pressedKey.
 * This value is updated every time after a key is read with getInput(). */
int getPressedKey ( void ) {
    return pressedKey;
}

/* getLandingStatus() returns the int value stored in landingStatus.
 * This value is updated at each lander time step. */
int getLandingStatus ( void ) {
    return landingStatus;
}

//void destroyLanders ( void ) {
//}
